package blackstar.bootstrap;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Logger;

/**
 * @author Ray (ayufox@gmail.com)
 */
public class Main
{
	private final static Logger LOGGER = Logger.getLogger("Main");

	protected static final String BLACKSTAR_HOME_TOKEN = "${BLACKSTAR_HOME}";

	private String homePath;

	private ClassLoader classLoader;

	private Properties properties = new Properties();

	public void init()
	{
		initHome();
		initProperties();
		initClassLoader();
	}

	private void initHome()
	{
		this.homePath = System.getenv("BLACKSTAR_HOME");
		if (this.homePath == null || "".equals(this.homePath))
		{
			File bootstrapJar = new File(System.getProperty("user.dir"),
					"blackstar.bootstrap.jar");
			if (bootstrapJar.exists())
			{
				try
				{
					this.homePath = (new File(System.getProperty("user.dir"),
							"..")).getCanonicalPath();
				} catch (IOException e)
				{
					this.homePath = System.getProperty("user.dir");
				}
			} else
			{
				this.homePath = System.getProperty("user.dir");
			}
		}

		LOGGER.info("HOME PATH:" + this.homePath);
	}

	private void initProperties()
	{
		File config = new File(this.homePath + File.separator + "config",
				"config.properties");
		if (config.exists())
		{
			try
			{
				this.properties.load(new FileInputStream(config));
			} catch (Exception e)
			{
				LOGGER.warning("load properties[" + config.getAbsolutePath()
						+ "] failed");
			}
		}
		else
		{
			LOGGER.info("file [" + config.getAbsolutePath() + "] not exist");
		}

		config = new File(this.homePath + File.separator + "config",
				"config.xml");
		if (config.exists())
		{
			try
			{
				this.properties.loadFromXML(new FileInputStream(config));
			} catch (Exception e)
			{
				LOGGER.warning("load xml properties["
						+ config.getAbsolutePath() + "] failed");
			}
		}
		else
		{
			LOGGER.info("file [" + config.getAbsolutePath() + "] not exist");
		}

		LOGGER.info("finish init properties");
	}

	private void initClassLoader()
	{
		String value = this.properties.getProperty("loadpath");
		if (value == null || "".equals(value))
		{
			value = "${BLACKSTAR_HOME}" + File.separator + "lib" + File.separator + "*.jar";
			LOGGER.info("loadpath config not found, use default [" + value + "]");
		}
		List<URL> urls = new ArrayList<URL>();
		int i;

		StringTokenizer tokenizer = new StringTokenizer(value, ",");
		while (tokenizer.hasMoreElements())
		{
			String repository = tokenizer.nextToken();

			while ((i = repository.indexOf(BLACKSTAR_HOME_TOKEN)) >= 0)
			{
				if (i > 0)
				{
					repository = repository.substring(0, i)
							+ this.homePath
							+ repository.substring(i
									+ BLACKSTAR_HOME_TOKEN.length());
				} else
				{
					repository = this.homePath
							+ repository.substring(BLACKSTAR_HOME_TOKEN
									.length());
				}
			}

			try
			{
				URL url = new URL(repository);
				urls.add(url);
				LOGGER.info("use url[" + url + "]");
				continue;
			} catch (MalformedURLException e)
			{
			}

			try
			{
				if (repository.endsWith("*.jar"))
				{
					LOGGER.info("use jars[" + repository + "]");
					repository = repository.substring(0, repository.length()
							- "*.jar".length());
					File directory = new File(repository);
					if (!directory.exists() || !directory.isDirectory()
							|| !directory.canRead())
						continue;
					File[] files = directory.listFiles(new FileFilter()
					{
						public boolean accept(File pathname)
						{
							return pathname.getName().endsWith(".jar");
						}
					});
					for (File file : files)
					{
						if (file.canRead())
						{
							URL url = file.toURI().toURL();
							urls.add(url);
							LOGGER.fine("add url[" + url + "]");
							url = null;
						}
					}
				} else if (repository.endsWith(".jar"))
				{
					LOGGER.info("use jar[" + repository + "]");
					URL url = new URL(repository);
					urls.add(url);
					LOGGER.fine("add url[" + url + "]");
					url = null;
				} else
				{
					LOGGER.info("use dir[" + repository + "]");
					URL url = new URL(repository);
					urls.add(url);
					LOGGER.fine("add url[" + url + "]");
					url = null;
				}
			} catch (MalformedURLException e)
			{
				LOGGER.warning("Error When Load [" + repository + "] : "
						+ e.getMessage());
			}
		}

		URL[] array = (URL[]) urls.toArray(new URL[urls.size()]);
		this.classLoader = new URLClassLoader(array, Thread.currentThread()
				.getContextClassLoader());
	}

	public void loadMain(String main, String[] args)
			throws ClassNotFoundException, SecurityException,
			NoSuchMethodException, IllegalArgumentException,
			IllegalAccessException, InvocationTargetException
	{
		Class mainClass = this.classLoader.loadClass(main);
		Method method = mainClass.getDeclaredMethod("main", String[].class);
		method.invoke(null, new Object[] { args });
	}

	public static void main(String[] args) throws Exception
	{
		if (args.length == 0)
		{
			LOGGER.warning("Error Argument.Useage : " + Main.class.getName()
					+ " mainClass ...");
		} else
		{
			String mainClass = args[0];
			String[] mainArgs = new String[args.length - 1];
			if (mainArgs.length > 0)
			{
				System.arraycopy(args, 1, mainArgs, 0, mainArgs.length);
			}
			Main main = new Main();
			main.init();
			main.loadMain(mainClass, mainArgs);
		}
	}
}
